#include "stdafx.h"

#include "Client.h"
#include "SingleUdxSendFileThread.h" //继承自 IUdxThreadSink
#include "database.h"
#include "minilzo.h"

	


Client::Client(void)
{
	
	
}


Client::~Client(void)
{
}
void Client::OnStreamLinkIdle(IUdxTcp *pTcp)
{
	//UpdateSpeed(pTcp);

}
void Client::OnStreamFinalRelease(IUdxTcp * pTcp)
{
	//UpdateSpeed2(pTcp);
}
void Client::FileTransmitDone(IUdxTcp*pTcp,char* filename,BOOL bSend)
{
	
	
}
void Client::OnStreamBroken(IUdxTcp * pTcp)
{

}

void Client::OnFileReadByts(IUdxTcp*pTcp,DWORD dwSize)
{
	//发送时 不会触发
	//getMyTool()->printStr("文件OnFileReadByts%ld字节",dwSize);
}
void Client::OnFileWriteByts(IUdxTcp*pTcp,DWORD dwSize)
{
	//发送文件时 会触发
	//getMyTool()->printStr("文件OnFileWriteByts%ld字节",dwSize);
}
void Client::OnFileContinue(IUdxTcp*pTcp,UINT64 startpoint,BOOL bSend)
{
	//不会触发断点续传
	getMyTool()->printStr("文件续传点%ld字节",startpoint);
}
void Client::OnEncoderByts(IUdxTcp*pTcp,BYTE*pData,DWORD dwSize)
{
// 经过试验确定 直接发包的小文件模式 不经过加密 大文件模式才经过加密	
}
/*
hzh
客户端收到服务端的回传消息后 调用这个函数
涉及到
1
2
3传输完毕 目前未发现服务端返回3
4 某个文件接收完毕可以删除了
5 大文件相对路径检查结果  如果是1 则可以传文件 后删除
6 大文件接收成功标志
7 大文件接收方接收失败
8 大文件可以发送了
  default 意外处理
*/
void Client::OnStreamRead(IUdxTcp * pTcp,BYTE* pData,int len)
{
	
	BYTE cmd = *pData;
	//char NowTime[32];
	GetUdxTools()->DBGStr("客户端收到OnStreamRead %d\n",cmd);
	switch(cmd)
	{
	case 1://file
		break;
	case 2://
		break;
	case 3:
		{
			//告诉对方发完了 对方回复收到 然后就可以关闭了
			//不管大小文件 都要执行
			pTcp->Close();
			GetUdxTools()->DBGStr("收到3指令 this link trans file done\n");
			//getMyTool()->printStr("");
		}
		break;
	case 4://服务端回执 哪个文件已经传完了 可以删除了 2023 0314 去掉删除过程 在发送时删除
		{
//			BYTE filenamelen = *(pData + 1);
//			char szFile[256] = {0};
//			memset(szFile,'\0',256);
//
//			memcpy(szFile,pData + 2,filenamelen);
//
//
//			char szNewFile[256] = {0};
//			memset(szNewFile,'\0',256);
//			sprintf(szNewFile,"%s/%s",(char*)m_sendir.c_str(),szFile);//拼接删除路径
//
//			//配置删除时才删除
//			if(m_delFile)
//			{
//				remove(szNewFile);//c语言的原生函数
//				GetUdxTools()->DBGStr("delete file %s - %d\n",szNewFile,filenamelen);
//			}	
		}
		break;
	case 5://服务端返回了大文件检查结果 此过程不使用了
		{

		}
		break;
	case 6://大文件发送成功返回6信息
		{
			if ( RELEASEFOR == "EN")
			{
				getMyTool()->printStr("recive reply 6,file trans done.");
			}else if(RELEASEFOR == "CH"){
			    getMyTool()->printStr("接收方返回6号信息，文件发送成功");
			}
			
			tcpTransFileState * m_pTcpTransFileState = (tcpTransFileState*) pTcp->GetUserData();
			m_pTcpTransFileState->transState=2;
		}
		break;
	case 7://大文件发送成功返回7信息
		{
			if ( RELEASEFOR == "EN")
			{
				getMyTool()->printStr("recive reply 7,file recive failed.");
			}else if(RELEASEFOR == "CH"){
			    getMyTool()->printStr("接收方返回7号信息，接收失败");
			}
			
			tcpTransFileState * m_pTcpTransFileState = (tcpTransFileState*) pTcp->GetUserData();
			m_pTcpTransFileState->transState=3;
		}
		break;
	case 8://大文件发送成功返回8信息
		{
			
			tcpTransFileState * m_pTcpTransFileState = (tcpTransFileState*) pTcp->GetUserData();
			memcpy(&m_pTcpTransFileState->transedLength,pData+1,sizeof(UINT64));
			m_pTcpTransFileState->transState=1;
			if ( RELEASEFOR == "EN")
			{
				getMyTool()->printStr("recive reply 8，while trans from broken point %I64u",m_pTcpTransFileState->transedLength);
			}else if(RELEASEFOR == "CH"){
			   getMyTool()->printStr("接收方返回8号信息，断点位置 %I64u",m_pTcpTransFileState->transedLength);
			}

			
		}
		break;
	default:
		{
			GetUdxTools()->DBGStr("unknown cmd %u - %d\n",cmd,len);
		}
		break;
	}

}
void Client::OnStreamMsgRead(IUdxTcp * pTcp,BYTE* pData,int len)
{
	GetUdxTools()->DBGStr("recive Msg\n");
	
	

}
void Client::DoWork(IFastUdx*pUdx,
					char* g_serverip,
					int g_serverport,
					int mode,
					char* g_senddir,
					BOOL g_delFile,
					int g_threadcount,
					BOOL g_bDeep)
{
	/////////////枚举文件
	int i = 0;
	int ptcount = __max(g_threadcount,1);//测试版本只能
	ptcount = __min(8,ptcount);//限定最大8线程 最小4线程
	
	list<string> listfile;//字符串列表 用push方法 添加元素
	MyListDir(g_senddir,listfile,g_bDeep);
	int filecounts = __min(listfile.size(),2000);
	GetUdxTools()->DBGStr("%s have %d files\n",g_senddir,filecounts);
	if(filecounts <= 0)
	{
		//没有文件就直接退出
		return ;
	}
	//////////分给N个线程去执行，每个线程执行的文件数
	int perthreadcount = filecounts / ptcount;
	if(perthreadcount == 0)
	{
		ptcount = 1;
		perthreadcount = filecounts;
	}

	if(ptcount >= 10)//进一步限制 防止破解 线程数不能超过10
	{
		GetUdxTools()->DBGStr("erro!!!!\n");
		return;

	}


	UdxListPtr pList[200] = {0};//文件列表数组 每个元素都有很多文件 最大200个线程 是在线程里边回收destroy
	UdxListPtr pListRecord[200] = {0};//用于记录文件以便输出数据库
	for ( i = 0;i< ptcount;i++)//创建对应线程数的list保存待发送文件名
	{
		pList[i] = CreateUdxList();//用来存放文件名
		pListRecord[i] = CreateUdxList();
		GetUdxTools()->DBGStr("建立文件列表\n");
	}

	//付值

	int ogrcount = perthreadcount;//每个线程应该负担的文件数量
	int listindex = 0;
	list<string>::iterator j; //j是liststring类型的迭代器
	string filename;

	int addcount = 0;
	//将每个队列 填满文件
	
	for(j = listfile.begin();j!=listfile.end();j++)//字符串列表 
	{
		filename = *j;

		//这个是udxlist
		pList[listindex]->AddBuff((BYTE*)filename.c_str(),filename.size());//名字放到buff中

		ogrcount --;//每个线程应该负担的文件数量 这里用来保证在该线程放的文件数

		addcount++;
		GetUdxTools()->DBGStr("pList %d add count %d - %s\n",listindex,addcount,filename.c_str());
		
		getMyTool()->printStr("Thread %d add %d file: - %s",listindex,addcount,filename.c_str());
		if(ogrcount < 0)
		{

			if(listindex < ptcount - 1)
			{
				listindex++;
				ogrcount = perthreadcount;
			}
		}
		if (addcount==2000)
		{
			break;
		}
	}

	UdxTcpPtr pTcpList[200] = {0}; //链接存放数组 最多200个

	UdxConnectInfo info;
	memset(&info,0,sizeof(info));
	int indexlist = 0;//成功创建的ptcp链接数量
	//创建对应数量的udxtcp链接
	for ( i = 0;i< ptcount;i++)
	{
		info.linkindex = i;
		//(m_serverip.GetBuffer(0),m_rport,FALSE,7,0,m_lostrate,m_strTennel.GetBuffer(0),&m_info)
		getMyTool()->printStr("Creating No [%d] link ",i);
		IUdxTcp* pTcp = pUdx->Connect(g_serverip,g_serverport,TRUE,0,m_limite*1024*1024,100,0,&info,0,FALSE);//3 true同步 链接成功才返回 稳定的状态最后参数 autorelease1 计数不加1 不用手动释放 
		// 最后参数为false 必须 有至少一个releaselife close 或者destroy
		//virtual void SetTimeOut(int con,int hardbeat,int contimeout) = 0;		//设置超时，分别为，联接超时，心跳间隔，和联接保活时间，秒为单位
		//pTcp->SetTimeOut(30,2,30);
		//pTcp->GetUdxCfg()->hasbp = 1;	
		//如果创建成功 则将该链接添加到指针数组中
		if(pTcp)
		{
			//增加延时 否则会判断链接不准确
			
			GetUdxTools()->UdxSleep(500);
			//20231117 修改 ptcp非空时才可以修改submss值
			pTcp->GetUdxCfg()->submss=1024;
			if(pTcp->IsConnected())
			{
				pTcp->SetFSink(this);//设置文件传输接口		
				//限制发送速度
				//pTcp->GetUdxCfg()->expectbew=g_SpeedMB*1024*1024;
				pTcpList[indexlist++] = pTcp;
				GetUdxTools()->DBGStr("AddTcp %d - %u\n",indexlist,pTcp->GetStreamID());

				if ( RELEASEFOR == "EN")
				{
					getMyTool()->printStr("Create %d link - ID%u",indexlist,pTcp->GetStreamID());
				}else if(RELEASEFOR == "CH"){
				   getMyTool()->printStr("通道连接 %d - ID%u 创建成功",indexlist,pTcp->GetStreamID());
				}

				
				pTcp->ReleaseLife();//已经加1 所以要释放
			}else
			{
				if ( RELEASEFOR == "EN")
				{
					getMyTool()->printStr("IP %s port %d tcp instance OK but cannot connect",g_serverip,g_serverport);
				}else if(RELEASEFOR == "CH"){
				   getMyTool()->printStr("IP %s port %d tcp已创建但链接失败",g_serverip,g_serverport);
				}
				
				//pTcp->ReleaseLife();//已经加1 所以要释放
			    //pTcp->Close();
				pTcp->Destroy();
			
			}
			

		}else
		{
			GetUdxTools()->DBGStr("server %s - %d can not connect\n",g_serverip,g_serverport);
			if ( RELEASEFOR == "EN")
				{
					getMyTool()->printStr("tcp instance IP %s port %d failed",g_serverip,g_serverport);
				}else if(RELEASEFOR == "CH"){
				    getMyTool()->printStr("链接 IP %s port %d 创建失败",g_serverip,g_serverport);
				}
			
		}
	}

	
	SingleUdxSendFileThread * pSend[100] = {0};//文件发送线程 最多100个
	char tcpState[100];
	memset(tcpState,'1',100);//全都初始化为字符1
	//这里进入循环的是创建成功 并链接成功的ptcp数量
	for (i = 0;i<indexlist;i++)
	{
		GetUdxTools()->DBGStr("创建[%d]条发送线程\n",i+1);
		pSend[i] = new SingleUdxSendFileThread(pTcpList[i],pList[i],mode,g_senddir,i,pListRecord[i],g_delFile,pUdx,&tcpState[i],m_bLZO);//tcp链接 文件队列 模式 发送目录 i 
		//GetUdxTools()->DBGStr("tcp释放一次\n");
		//这里释放一次的操作要早于线程中的destroy操作
		//pTcpList[i]->ReleaseLife();//主程序要减一 20231117 在链接成功时 释放一次
		//GetUdxTools()->DBGStr("tcp释放一次完毕\n");
	}
	//已完成的tcp链接数 小于等于总的链接数 就一直循环下去
	int haveSendDownTcp=0;
	while (haveSendDownTcp<indexlist)
	{
		GetUdxTools()->DBGStr("进入while 并开启事物\n");
		beginBusiness();
		for (i = 0;i<indexlist;i++)
		{
			
			sendFileRecord sendfile;//结构体
			IUdxBuff* pFile = NULL;
			while(1)
			{	
				if (pListRecord[i]!=NULL)
				{
					pFile = pListRecord[i]->GetBuff();//用于记录日志信息的列表 通过形参形式带入处理线程
					if(!pFile)
						break;
					memcpy(&sendfile,pFile->GetBuff(),pFile->GetLen());
					pFile->Destroy();
					insertLog("clientlog",g_senddir,sendfile.sendTime,sendfile.fileName,sendfile.speed,sendfile.fsizeB,sendfile.ms);
				}else{
					break;
				}
				
				
			}
			//确认一个链接的日志都计入数据库了
			if (pListRecord[i]!=NULL&&tcpState[i]=='0'&&!pListRecord[i]->GetBuff())
			{
				haveSendDownTcp++;
				GetUdxTools()->DBGStr("线程-%d-结束一条链接的数据库写入，并销毁信息记录器%d\n",i,haveSendDownTcp);
				pListRecord[i]->Destroy();
				pListRecord[i]=NULL;
				//GetUdxTools()->DBGStr("线程-%d-离开for\n",i);
			}		
		}
		commitBusiness();
		GetUdxTools()->UdxSleep(1000);//这一轮写完了 就休息一下

	}
	GetUdxTools()->DBGStr("链接的数据库写入完成\n",haveSendDownTcp);

	for (i = 0;i<indexlist;i++)
	{
		GetUdxTools()->DBGStr("执行delete 发送进程\n");
		delete pSend[i];
		GetUdxTools()->DBGStr("delete 发送进程完毕\n");
		pSend[i] = 0;		
	}
	GetUdxTools()->DBGStr("dowork执行完毕\n");

}
